home *** CD-ROM | disk | FTP | other *** search
- /* grep - search a file for a pattern Author: Norbert Schlenker */
-
- /* Norbert Schlenker (nfs@princeton.edu) 1990-02-08
- * Released into the public domain.
- *
- * Grep searches files for lines containing a pattern, as specified by
- * a regular expression, and prints those lines. It is invoked by:
- * grep [flags] [pattern] [file ...]
- *
- * Flags:
- * -e pattern useful when pattern begins with '-'
- * -c print a count of lines matched
- * -i ignore case
- * -l prints just file names, no lines (quietly overrides -n)
- * -n printed lines are preceded by relative line numbers
- * -s prints errors only (quietly overrides -l and -n)
- * -v prints lines which don't contain the pattern
- *
- * Semantic note:
- * If both -l and -v are specified, grep prints the names of those
- * files which do not contain the pattern *anywhere*.
- *
- * Exit:
- * Grep sets an exit status which can be tested by the caller.
- * Note that these settings are not necessarily compatible with
- * any other version of grep, especially when -v is specified.
- * Possible status values are:
- * 0 if any matches are found
- * 1 if no matches are found
- * 2 if syntax errors are detected or any file cannot be opened
- */
-
-
- /* External interfaces */
- #include <sys/types.h>
- #include "regex.h" /* Thanks to Henry Spencer */
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <unistd.h>
-
- /* Internal constants */
- #define MATCH 0 /* exit code: some match somewhere */
- #define NO_MATCH 1 /* exit code: no match on any line */
- #define FAILURE 2 /* exit code: syntax error or bad file name */
-
- /* Macros */
- #define SET_FLAG(c) (flags[(c)-'a'] = 1)
- /* [<][>][^][v][top][bottom][index][help] */
- #define FLAG(c) (flags[(c)-'a'] != 0)
- /* [<][>][^][v][top][bottom][index][help] */
-
- #define uppercase(c) (((unsigned) ((c) - 'A')) <= ('Z' - 'A'))
- /* [<][>][^][v][top][bottom][index][help] */
- #define downcase(c) ((c) - 'A' + 'a')
- /* [<][>][^][v][top][bottom][index][help] */
-
- /* Private storage */
- static char *program; /* program name */
- static char flags[26]; /* invocation flags */
- static regexp *expression; /* compiled search pattern */
-
- /* External variables. */
- extern int optind;
- extern char *optarg;
-
- /* Internal interfaces */
- int main (int argc, char **argv);
- static int match (FILE *input, char *label, char *filename);
- static char *get_line (FILE *input);
- static char *map_nocase (char *line);
- static void error_exit (char *msg);
- void regerror (char *s ) ;
-
-
- int main(argc, argv)
- /* [<][>][^][v][top][bottom][index][help] */
- int argc;
- char *argv[];
- {
- int opt; /* option letter from getopt() */
- char *pattern; /* search pattern */
- int exit_status = NO_MATCH; /* exit status for our caller */
- int file_status; /* status of search in one file */
- FILE *input; /* input file (if not stdin) */
-
- program = argv[0];
- memset(flags, 0, sizeof(flags));
- pattern = NULL;
-
- /* Process any command line flags. */
- while ((opt = getopt(argc, argv, "e:cilnsv")) != EOF) {
- if (opt == '?')
- exit_status = FAILURE;
- else
- if (opt == 'e')
- pattern = optarg;
- else
- SET_FLAG(opt);
- }
-
- /* Detect a few problems. */
- if ((exit_status == FAILURE) || (optind == argc && pattern == NULL))
- error_exit("Usage: %s [-cilnsv] [-e] expression [file ...]\n");
-
- /* Ensure we have a usable pattern. */
- if (pattern == NULL)
- pattern = argv[optind++];
-
- /* Map pattern to lowercase if -i given. */
- if (FLAG('i')) {
- char *p;
- for (p = pattern; *p != '\0'; p++) {
- if (uppercase(*p))
- *p = downcase(*p);
- }
- }
-
- if ((expression = regcomp(pattern)) == NULL)
- error_exit("%s: bad regular expression\n");
-
- /* Process the files appropriately. */
- if (optind == argc) { /* no file names - find pattern in stdin */
- exit_status = match(stdin, (char *) NULL, "<stdin>");
- }
- else
- if (optind + 1 == argc) { /* one file name - find pattern in it */
- if (strcmp(argv[optind], "-") == 0) {
- exit_status = match(stdin, (char *) NULL, "-");
- } else {
- if ((input = fopen(argv[optind], "r")) == NULL) {
- fprintf(stderr, "%s: couldn't open %s\n",
- program, argv[optind]);
- exit_status = FAILURE;
- }
- else {
- exit_status = match(input, (char *) NULL, argv[optind]);
- }
- }
- }
- else
- while (optind < argc) { /* lots of file names - find pattern in all */
- if (strcmp(argv[optind], "-") == 0) {
- file_status = match(stdin, "-", "-");
- } else {
- if ((input = fopen(argv[optind], "r")) == NULL) {
- fprintf(stderr, "%s: couldn't open %s\n",
- program, argv[optind]);
- exit_status = FAILURE;
- } else {
- file_status = match(input, argv[optind], argv[optind]);
- fclose(input);
- }
- }
- if (exit_status != FAILURE)
- exit_status &= file_status;
- ++optind;
- }
- return(exit_status);
- }
-
-
- /* match - matches the lines of a file with the regular expression.
- * To improve performance when either -s or -l is specified, this
- * function handles those cases specially.
- */
-
- static int match(input, label, filename)
- /* [<][>][^][v][top][bottom][index][help] */
- FILE *input;
- char *label;
- char *filename;
- {
- char *line, *testline; /* pointers to input line */
- long int lineno = 0; /* line number */
- long int matchcount = 0; /* lines matched */
- int status = NO_MATCH; /* summary of what was found in this file */
-
- if (FLAG('s') || FLAG('l')) {
- while ((line = get_line(input)) != NULL) {
- testline = FLAG('i') ? map_nocase(line) : line;
- if (regexec(expression, testline, 1)) {
- status = MATCH;
- break;
- }
- }
- if (FLAG('l'))
- if ((!FLAG('v') && status == MATCH) ||
- ( FLAG('v') && status == NO_MATCH))
- puts(filename);
- return status;
- }
-
- while ((line = get_line(input)) != NULL) {
- ++lineno;
- testline = FLAG('i') ? map_nocase(line) : line;
- if (regexec(expression, testline, 1)) {
- status = MATCH;
- if (!FLAG('v')) {
- if (label != NULL)
- printf("%s:", label);
- if (FLAG('n'))
- printf("%ld:", lineno);
- if (!FLAG('c')) puts(line);
- matchcount++;
- }
- } else {
- if (FLAG('v')) {
- if (label != NULL)
- printf("%s:", label);
- if (FLAG('n'))
- printf("%ld:", lineno);
- if (!FLAG('c')) puts(line);
- matchcount++;
- }
- }
- }
- if (FLAG('c')) printf("%ld\n", matchcount);
- return status;
- }
-
-
- /* get_line - fetch a line from the input file
- * This function reads a line from the input file into a dynamically
- * allocated buffer. If the line is too long for the current buffer,
- * attempts will be made to increase its size to accomodate the line.
- * The trailing newline is stripped before returning to the caller.
- */
-
- #define FIRST_BUFFER (size_t)256 /* first buffer size */
-
- static char *buf = NULL; /* input buffer */
- static size_t buf_size = 0; /* input buffer size */
-
- static char *get_line(input)
- /* [<][>][^][v][top][bottom][index][help] */
- FILE *input;
- {
- int n;
- register char *bp;
- register int c;
- char *new_buf;
- size_t new_size;
-
- if (buf_size == 0) {
- if ((buf = (char *) malloc(FIRST_BUFFER)) == NULL)
- error_exit("%s: not enough memory\n");
- buf_size = FIRST_BUFFER;
- }
-
- bp = buf;
- n = buf_size;
- while (1) {
- while (--n > 0 && (c = getc(input)) != EOF) {
- if (c == '\n') {
- *bp = '\0';
- return buf;
- }
- *bp++ = c;
- }
- if (c == EOF)
- return (ferror(input) || bp == buf) ? NULL : buf;
- new_size = buf_size << 1;
- if ((new_buf = (char *) realloc(buf, new_size)) == NULL) {
- fprintf(stderr, "%s: line too long - truncated\n", program);
- while ((c = getc(input)) != EOF && c != '\n') ;
- *bp = '\0';
- return buf;
- } else {
- bp = new_buf + (buf_size - 1);
- n = buf_size + 1;
- buf = new_buf;
- buf_size = new_size;
- }
- }
- }
-
-
- /* map_nocase - map a line down to lowercase letters only.
- * bad points: assumes line gotten from get_line.
- * there is more than A-Z you say?
- */
-
- static char *map_nocase(line)
- /* [<][>][^][v][top][bottom][index][help] */
- char *line;
- {
- static char *mapped;
- static size_t map_size = 0;
- char *mp;
-
- if (map_size < buf_size) {
- if (map_size == 0) {
- mapped = (char *) malloc(buf_size);
- } else {
- mapped = (char *) realloc(mapped, buf_size);
- }
- if (mapped == NULL)
- error_exit("%s: not enough memory\n");
- map_size = buf_size;
- }
-
- mp = mapped;
- do {
- *mp++ = uppercase(*line) ? downcase(*line) : *line;
- } while (*line++ != '\0');
-
- return mapped;
- }
-
-
- /* Regular expression code calls this routine to print errors. */
-
- void regerror(s)
- /* [<][>][^][v][top][bottom][index][help] */
- char *s;
- {
- fprintf(stderr, "regexp: %s\n", s);
- }
-
-
- /* Common exit point for outrageous errors. */
-
- static void error_exit(msg)
- /* [<][>][^][v][top][bottom][index][help] */
- char *msg;
- {
- fprintf(stderr, msg, program);
- exit(FAILURE);
- }
- /* [<][>][^][v][top][bottom][index][help] */
-